home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / oper_sys / presto / prest_04.lha / src / scheduler.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-06-06  |  7.0 KB  |  390 lines

  1.  
  2. /*
  3.  * scheduler.c
  4.  *
  5.  *    Implementation of shared scheduler class.  All process
  6.  *    objects interact with the scheduler object trying to
  7.  *    getreadythreads off the ready queue.
  8.  *
  9.  *    ThreadPool trivially defines the base class for the shared
  10.  *    ready thread pool.
  11.  *
  12.  *    ThreadPoolQueue defines the default scheduling discipline.  
  13.  *    Very simple, no priorities.... nothing fancy.
  14.  *
  15.  *    Last modified:        1/1/88
  16.  *    by:            bnb
  17.  *    reason:            take care for preschedthreads
  18.  *
  19.  *    Last modified:        12/21/87
  20.  *    by:            bnb
  21.  *    reason:            add these comments
  22.  *
  23.  */
  24.  
  25. #define _SCHEDULER_C
  26.  
  27. #include <sys/types.h>
  28. #include <sys/signal.h>
  29. #include <osfcn.h>
  30. #include <stream.h>
  31. #include "presto.h"
  32.  
  33.  
  34.  
  35. //
  36. // preschedthreads queue is where threads that get created
  37. // before the scheduler exists get placed.  On scheduler
  38. // creation, these guys are moved to the real thread pool.
  39. // This allows threads to be created in static constructors without
  40. // requiring that the entire system be in place.
  41. //
  42.  
  43.  
  44. shared_t ThreadQ    *preschedthreads = 0;
  45.  
  46.  
  47. //
  48. // Definition of default ThreadPool{Queue} type known to scheduler.
  49. //
  50.  
  51.  
  52. ThreadPool::ThreadPool()
  53. {
  54. }
  55.  
  56. ThreadPool::~ThreadPool()
  57. {
  58. }
  59.  
  60. Thread*
  61. ThreadPool::get()
  62. {
  63.     error("Base threadpool get");
  64.     // NOTREACHED
  65.     return 0;
  66. }
  67.  
  68. void
  69. ThreadPool::insert(Thread* t)
  70. {
  71.     error("Base threadpool get");
  72.     // NOTREACHED
  73.     t = t;        // Satisfy cfront
  74. }
  75.  
  76. int
  77. ThreadPool::size()
  78. {
  79.     error("Base threadpool size");
  80.     // NOTREACHED
  81.     return 1;
  82. }
  83.  
  84.  
  85.  
  86. ThreadPoolQueue::ThreadPoolQueue()
  87. {
  88.     tp_tq = new ThreadQ(TS_READY);
  89. }
  90.  
  91. ThreadPoolQueue::~ThreadPoolQueue()
  92. {
  93.     delete tp_tq;
  94. }
  95.  
  96.  
  97. Thread*
  98. ThreadPoolQueue::get()
  99. {
  100.     register ThreadQ* tq = tp_tq;        // speed hack
  101.     return tq->get();
  102. }
  103.  
  104. void
  105. ThreadPoolQueue::insert(Thread* t)
  106. {
  107.     register ThreadQ* tq = tp_tq;
  108.     tq->append(t);
  109. }
  110.  
  111. int
  112. ThreadPoolQueue::size()
  113. {
  114.     return tp_tq->length();
  115. }
  116.  
  117.  
  118.  
  119.  
  120. //
  121. // The main scheduler system
  122. //
  123.  
  124. Scheduler::Scheduler(int numschedulers, int quantum = DEFQUANTUM)
  125. {
  126. #ifdef PREEMPT
  127.     void sigpreempt_init();
  128. #endif PREEMPT
  129.  
  130.     sc_p_numschedulers = numschedulers;
  131.     //
  132.     // nullify the threads
  133.     //
  134.     sc_t_ready = new ThreadPoolQueue;
  135.  
  136.     //
  137.     // Only create a process context if one doesn't already exist.
  138.     // User can create his own in Main::init().  Allows us to
  139.     // virtualize the constructor.  
  140.     //
  141.     if (thisproc == 0)
  142.         sc_p_procs[0] = new Process(P_ROOT, 0);
  143.     else
  144.         sc_p_procs[0] = thisproc;
  145.  
  146.     sc_p_activeschedulers = 1;
  147.     sc_p_busybits = 0x0;
  148.     sc_lock = new Spinlock;
  149.     sc_quantum = quantum;
  150.  
  151. #ifdef PREEMPT
  152.     if (sc_quantum)
  153.         sigpreempt_init();
  154. #endif PREEMPT
  155. }
  156.  
  157. //
  158. // Scheduler invocation function.
  159. //     Create sc_p_numschedulers threads and bind each one of them
  160. //    to a process invocation.  Schedule those threads to start running
  161. //    which will in turn run a new scheduler on some new processor.
  162. //
  163. //    In case we have any preschedthreads, schedule them here.
  164. //
  165.  
  166. int
  167. Scheduler::invoke()
  168. {
  169.     extern int cpus_online();
  170.     extern void strcpy(char*, char*);
  171.     int cpusonline = cpus_online();
  172.     int pid;
  173.  
  174. #ifdef PREEMPT
  175.     extern void sigpreempt_beginclock(struct timeval *q);
  176.     extern int preemption_enabled;
  177. #endif PREEMPT
  178.  
  179.     if (sc_p_numschedulers < 0)
  180.         return sc_p_activeschedulers;
  181.  
  182.     if (sc_p_numschedulers > cpusonline)
  183.         sc_p_numschedulers = cpusonline;
  184.  
  185.     //
  186.     // Initialize the process pool
  187.     //
  188.  
  189.     initsighandlers(0);
  190.  
  191.     for (pid = sc_p_activeschedulers; pid < sc_p_numschedulers; pid++) {
  192.  
  193. #define XNAME "proc_X"
  194.         char *pname = new char[sizeof(XNAME)];
  195.         strcpy(pname, XNAME);
  196.         pname[5] = pid + 'a' - 1;    
  197.         sc_p_procs[pid] = thisproc->newprocess(pname,pid+thisproc->id());
  198.         if (sc_p_procs[pid]->state() & S_ERROR)    {
  199.             perror("Scheduler::new Process");
  200.             sc_p_numschedulers = --pid;
  201.             this->abort(SIGKILL);
  202.             kill(getpid(), SIGILL);
  203.             // not reached
  204.         }
  205.         sc_p_busybits |= 1<<pid;    // assume busy
  206.     } 
  207.  
  208.     initsighandlers(1);            // prepare parent to clean up
  209. #ifdef PREEMPT
  210.     if (sc_quantum)    {
  211.         struct timeval    q;
  212.         q.tv_sec = sc_quantum / 1000;        // in msecs
  213.         q.tv_usec = (sc_quantum % 1000) * 1000;    // 1msec = 1000usec
  214.         sigpreempt_beginclock(&q);
  215.     }    
  216. #endif PREEMPT
  217.  
  218.     //
  219.     // take care of threads created in the prescheduler era
  220.     //
  221.  
  222.     if (preschedthreads)    {
  223.         Thread *t;
  224.         while (t=preschedthreads->get())
  225.             resume(t);
  226.         delete preschedthreads;
  227.         preschedthreads = 0;
  228.     }
  229.  
  230.     return (sc_p_activeschedulers = sc_p_numschedulers);
  231. }
  232.  
  233.  
  234. Scheduler::~Scheduler()
  235. {
  236.     delete sc_t_ready;
  237. }
  238.  
  239.  
  240. void
  241. Scheduler::halt()
  242. {
  243.     int pid;
  244.  
  245. #ifdef PREEMPT
  246.     extern void sigpreempt_stopclock();
  247.  
  248.     if (sc_quantum)
  249.         sigpreempt_stopclock();
  250. #endif PREEMPT
  251.  
  252.     /*
  253.      * Stop listening for dead children -- otherwise can race with "master"
  254.      * and get spurious "exit" messages from schedulerReapChild().
  255.      */
  256.  
  257.     initsighandlers(-1);                // turn off SIGCHLD
  258.  
  259.     /*
  260.      * Ask the kids to quietly die.
  261.      */
  262.  
  263.     for (pid = 1; pid < sc_p_numschedulers; pid++)
  264.         sc_p_procs[pid]->request( R_RETURN /*R_DIE*/);
  265.  
  266.     sc_p_procs[0]->request(R_RETURN);    // cause main to return
  267. }
  268.  
  269.  
  270.  
  271. //
  272. // Resume a thread within a process.
  273. // If this is a virgin thread, then the thread code starts off a little
  274. // but differently.  See threads.c
  275. //
  276.  
  277. void
  278. Scheduler::resume(Thread *t)
  279. {
  280.     if (t->flags()&TF_SCHEDULER)
  281.         t->error("Can't resume a scheduler thread\n");
  282.     t->isready();
  283.     sc_t_ready->insert(t);        // some process should grab me
  284. }
  285.  
  286.  
  287.  
  288.  
  289. //
  290. // Have to serialize access here so we can know when the system stops.
  291. // Should really be more sophisticated as to determining when everything
  292. // is done.
  293. //
  294.  
  295. Thread*
  296. Scheduler::getreadythread()
  297. {
  298.     sc_lock->lock();
  299.  
  300.     Thread *t = sc_t_ready->get();
  301.  
  302.     if (t)    {
  303.         (void)busybits(1);
  304.         sc_lock->unlock();
  305.     } else    {
  306.         int busy = busybits(0);
  307.         sc_lock->unlock();        
  308.         //
  309.         // If no busy procs, and we are the master....
  310.         //
  311.         if (busy == 0 && thisproc->isroot() )
  312.             this->halt();
  313.     }
  314.     return t;
  315. }
  316.  
  317.  
  318. void
  319. Scheduler::error(char *s)
  320. {
  321.     cerr << "Scheduler error " << s << "\n" << " in " << hex(long(this)) << "\n";
  322.     fatalerror();
  323. }
  324.  
  325.  
  326. ThreadPool*
  327. Scheduler::getreadypool()
  328. {
  329.     return sc_t_ready; 
  330. }
  331.  
  332.  
  333. //
  334. // swap new ready pool.  Order of switch is important here.
  335. //    First, replace old pool with new pool.
  336. //    Then,  move any threads from old pool to new pool
  337. //    Them,  delete old pool.
  338. //
  339.  
  340. ThreadPool*
  341. Scheduler::setreadypool(ThreadPool* newtp)
  342. {
  343.     ThreadPool* oldpool ;
  344.     Thread*    t;
  345.  
  346.     if (!newtp)
  347.         error("Bad arg to newreadypool");
  348.  
  349.     oldpool = sc_t_ready;        // can still be active
  350.     sc_t_ready = newtp;
  351.                     // we expect to have only reference to
  352.                     // oldpool by this point.
  353.     if (oldpool)    {
  354.         while (t=oldpool->get())
  355.             sc_t_ready->insert(t);
  356.     }
  357.  
  358.     return oldpool;
  359. }
  360.  
  361. void
  362. Scheduler::print(ostream& s)
  363. {
  364.     int i;
  365.  
  366.     s << form("(Scheduler):0x%x, sc_t_ready=0x%x, ", this, sc_t_ready);
  367.     s << form("sc_p_activescheduler=%d, sc_p_busybits=%d, sc_quantum=%d",
  368.             sc_p_activeschedulers, sc_p_busybits, sc_quantum);
  369.     s << sc_lock << "\n";
  370.     for (i = 0; i < sc_p_activeschedulers; i++)
  371.         s << sc_p_procs[i] << "\n";
  372. }
  373.  
  374. //
  375. // global version of abort.  If we are the scheduler, then knock
  376. // everyone else off first, otherwise just knock ourselves off and
  377. // reply on the scheduler to pick us up.
  378. //
  379. int
  380. abort()
  381. {
  382.     if (sched)    {
  383.         sched->abort(SIGILL);
  384.         // NOT REACHED
  385.     } else            
  386.         kill(getpid(), SIGILL);        // core dump
  387.     return 0;                // not reached
  388. }
  389.  
  390.